﻿using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System.Linq;
using matrix = Microsoft.Xna.Framework.Matrix;

namespace XnaCanvas {
    public class XModel : XObject {
        public void ResetNomal3() {
            var count = indices.Count - 3;
            for (int i = 0; i < count; i += 3) {
                var x1 = vertices[indices[i]].position - vertices[indices[i + 1]].position;
                var x2 = vertices[indices[i]].position - vertices[indices[i + 2]].position;
                var x3 = Vector3.Cross(x1, x2);
                vertices[indices[i]]=vertices[indices[i]].SetNormal(x3);
                vertices[indices[i + 1]]=vertices[indices[i + 1]].SetNormal(x3);
                vertices[indices[i + 2]]=vertices[indices[i + 2]].SetNormal(x3);
            }
        }
        public override void Present() {
            if (GetState(currentState) > 0) {
                OnStateAction(currentState);
                stateTable[currentState]--;
            }
            if (this.indices.Count > 0) {
                this.SetData();
                effect.World = World;
                effect.View = xsence.viewer;
                effect.Projection = xsence.projection;
                effect.DiffuseColor = xsence.diffuseColor.ToVector3();
                effect.Alpha = xsence.diffuseColor.A / 255.0f;
                XSence.Device.SetVertexBuffer(this.vbuffer);
                XSence.Device.Indices = ibuffer;
                foreach (var pass in effect.CurrentTechnique.Passes) {
                    pass.Apply();
                    XSence.Device.DrawIndexedPrimitives(PrimitivType, 0, 0, vertices.Count, 0, primitiveCount);
                }
            } else {
                foreach (var item in parts) {
                    item.XObj.Present();
                }
            }
        }
        public XModel(PrimitiveType pt = PrimitiveType.TriangleList) {
            this.vertices = new List<VertexPositionNormalColor>();
            this.indices = new List<ushort>();
            base.PrimitivType = pt;
            this.isDynamicIV = true;
            parts = new List<XObjectUnit>();
            PartCount = 0;
            VertextCount = 0;
            stateTable = new Dictionary<string, ushort>();
        }
        //public event EventHandler<PartsEventArgs> PartsSitSetting;
        //protected virtual void OnPartsSitSetting() {
        //    if (PartsSitSetting != null) {
        //        PartsSitSetting.Invoke(this, new PartsEventArgs(this.parts));
        //    }
        //}
        public string currentState;
        public Dictionary<string, ushort> stateTable;
        public event Action<string, XModel> StateAction;
        /// <summary>
        /// 让模型可以根据用户输入而发生变化
        /// </summary>
        /// <param name="stateid"></param>
        protected void OnStateAction(string stateid) {
            if (StateAction != null) {
                StateAction(stateid, this);
            }
        }
        protected ushort GetState(string stateId) {
            if (string.IsNullOrEmpty(stateId)) {
                return 0;
            } else if (stateTable.Keys.Contains(stateId)) {
                return stateTable[stateId];
            } else {
                return 0;
            }
        }
        public event Action<XModel> ModelIndexSetting;
        protected virtual void OnModelIndexSetting() {
            if (ModelIndexSetting != null) {
                ModelIndexSetting.Invoke(this);
                ModelIndexSetting = null;
            }
        }
        public override matrix World {
            get { return base.World; }
            set {
                base.World = value;
                if (parts != null)
                    foreach (var item in parts) {
                        item.XObj.World = value;
                    }
            }
        }
        public int PartCount;
        public int VertextCount;
        public XObjectUnit AddParts(XObj2 xobj2, string id = "", string partName = "") {
            var xob = new XObjectUnit(id, partName, xobj2, parts.Count, this);
            parts.Add(xob);
            PartCount++;
            return xob;
        }
        public XObjectUnit AddParts(XObj3 xobj3, string id = "", string partName = "") {
            var xob = new XObjectUnit(id, partName, xobj3, parts.Count, this);
            parts.Add(xob);
            PartCount++;
            return xob;
        }
        public override void SetData() {
            vertices.Clear();
            VertextCount = 0;
            foreach (var item in parts) {
                foreach (var item2 in item.XObj.vertices) {
                    vertices.Add(item2);
                    VertextCount++;
                }
            }
            OnModelIndexSetting();
            if (this.PrimitivType == PrimitiveType.TriangleList || this.PrimitivType == PrimitiveType.TriangleStrip)
                ResetNomal3();
            base.SetData();
        }
        public override void ClearBuffer() {
            VertextCount = 0;
            base.ClearBuffer();
        }
        List<XObjectUnit> parts;
        public XObjectUnit this[int index] {
            get {
                var p = from item in parts where item.index == index select item;
                return p.First();
            }
        }
        public IEnumerable<XObjectUnit> this[string partName] {
            get {
                var p = from item in parts where item.partName == partName select item;
                return p;
            }
        }
        public XObjectUnit this[string partName, string id] {
            get {
                var p = from item in parts where item.partName == partName && item.id == id select item;
                return p.First();
            }
        }
    }
    public class XObjectUnit {
        public string partName;
        public string id;
        public int index;
        public XObj2 xobj2;
        public XObj3 xobj3;
        public XObject XObj {
            get {
                return (XObject)xobj2 ?? xobj3;
            }
        }
        internal XObjectUnit(string name, string partName, XObj2 xobj2, int num, XModel xm) {
            this.id = name; this.partName = partName; this.xobj2 = xobj2; this.index = num;
            xobj2.xsence = xm.xsence;
        }
        internal XObjectUnit(string name, string partName, XObj3 xobj3, int num, XModel xm) {
            this.id = name; this.partName = partName; this.xobj3 = xobj3; this.index = num;
            xobj3.xsence = xm.xsence;
        }
    }
    public class PartsEventArgs : EventArgs {
        public List<XObjectUnit> parts;
        public PartsEventArgs(List<XObjectUnit> e) {
            this.parts = e;
        }
    }
}
